package top.wilsonlv.jaguar.commons.data.encryption.util;

import top.wilsonlv.jaguar.commons.data.encryption.coder.DesCoder;
import top.wilsonlv.jaguar.commons.data.encryption.coder.RsaCoder;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.regex.Pattern;


/**
 * 数据加密辅助类(默认编码UTF-8)
 *
 * @author lvws
 * @since 2019-02-27
 */
public final class EncryptionUtil {

    private static final Pattern PASSWORD_PATTERN = Pattern.compile("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(.{6,20})$");

    private static final char[] UPPER_CASE_LETTER = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
    private static final char[] LOWER_CASE_LETTER = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
    private static final char[] NUM = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};

    private static final char[] HEX_CHARS =
            {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    private EncryptionUtil() {
    }

    /**
     * 验证密码格式
     */
    public static boolean passwordUnmatched(String password) {
        return !PASSWORD_PATTERN.matcher(password).matches();
    }

    public static String randomCode(int upperCaseLetterLength, int lowerCaseLetterLength, int numLength) {
        Random r = new Random();

        List<Character> characters = new ArrayList<>(8);
        for (int i = 0; i < upperCaseLetterLength; i++) {
            characters.add(UPPER_CASE_LETTER[r.nextInt(UPPER_CASE_LETTER.length)]);
        }

        for (int i = 0; i < lowerCaseLetterLength; i++) {
            characters.add(LOWER_CASE_LETTER[r.nextInt(LOWER_CASE_LETTER.length)]);
        }

        for (int i = 0; i < numLength; i++) {
            characters.add(NUM[r.nextInt(NUM.length)]);
        }

        Collections.shuffle(characters);

        StringBuilder builder = new StringBuilder();
        for (Character character : characters) {
            builder.append(character);
        }
        return builder.toString();
    }

    public static byte[] md5(String data) {
        try {
            return MessageDigest.getInstance("MD5").digest(data.getBytes(StandardCharsets.UTF_8));
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalArgumentException(e);
        }
    }

    public static String encodeHex(byte[] bytes) {
        char[] chars = new char[32];
        for (int i = 0; i < chars.length; i = i + 2) {
            byte b = bytes[i / 2];
            chars[i] = HEX_CHARS[(b >>> 0x4) & 0xf];
            chars[i + 1] = HEX_CHARS[b & 0xf];
        }
        return new String(chars);
    }

    public static String md5AsHex(String data) {
        return encodeHex(md5(data));
    }

    /**
     * BASE64编码
     */
    public static String encryptBase64(byte[] data) {
        try {
            return new String(Base64.getEncoder().encode(data), StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException("加密错误，错误信息：", e);
        }
    }

    /**
     * BASE64解码
     */
    public static byte[] decryptBase64(String key) {
        try {
            return Base64.getDecoder().decode(key);
        } catch (Exception e) {
            throw new RuntimeException("解密错误，错误信息：", e);
        }
    }

    /**
     * DES对称加密
     */
    public static String encryptDes(String data, byte[] key) {
        try {
            return encryptBase64(DesCoder.encrypt(data, key));
        } catch (Exception e) {
            throw new RuntimeException("加密错误，错误信息：", e);
        }
    }

    /**
     * DES对称解密
     */
    public static String decryptDes(String encryptData, byte[] key) {
        try {
            return DesCoder.decrypt(decryptBase64(encryptData), key);
        } catch (Exception e) {
            throw new RuntimeException("解密错误，错误信息：", e);
        }
    }

    /**
     * RSA私钥签名
     */
    public static String signRsa(String data, String privateKey) {
        try {
            byte[] sign = RsaCoder.sign(data.getBytes(StandardCharsets.UTF_8), decryptBase64(privateKey));
            return encryptBase64(sign);
        } catch (Exception e) {
            throw new RuntimeException("签名错误，错误信息：", e);
        }
    }

    /**
     * RSA公钥验签
     */
    public static boolean verifyRsa(String data, String publicKey, String sign) {
        try {
            return RsaCoder.verify(data.getBytes(StandardCharsets.UTF_8), decryptBase64(publicKey), decryptBase64(sign));
        } catch (Exception e) {
            throw new RuntimeException("验签错误，错误信息：", e);
        }
    }

    /**
     * RSA公钥加密
     */
    public static String encryptRsaPublic(String data, String publicKey) {
        return encryptRsaPublic(data, decryptBase64(publicKey));
    }

    /**
     * RSA公钥加密
     */
    public static String encryptRsaPublic(String data, byte[] publicKey) {
        try {
            return encryptBase64(RsaCoder.encryptByPublicKey(data.getBytes(StandardCharsets.UTF_8), publicKey));
        } catch (Exception e) {
            throw new RuntimeException("加密错误，错误信息：", e);
        }
    }

    /**
     * RSA私钥解密
     */
    public static String decryptRsaPrivate(String cryptData, String privateKey) {
        return decryptRsaPrivate(cryptData, decryptBase64(privateKey));
    }

    /**
     * RSA私钥解密
     */
    public static String decryptRsaPrivate(String cryptData, byte[] privateKey) {
        try {
            // 把字符串解码为字节数组，并解密
            return new String(RsaCoder.decryptByPrivateKey(decryptBase64(cryptData), privateKey));
        } catch (Exception e) {
            throw new RuntimeException("解密错误，错误信息：", e);
        }
    }

    public static String sortParams(Map<String, String[]> params, String... ignores) {
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);

        Set<String> ignoreSet = new HashSet<>(Arrays.asList(ignores));

        StringBuilder rawStr = new StringBuilder();
        for (String key : keys) {
            if (ignoreSet.contains(key)) {
                continue;
            }

            List<String> values = Arrays.asList(params.get(key));
            Collections.sort(values);

            for (String value : values) {
                rawStr.append(key).append("=").append(value).append("&");
            }
        }

        return rawStr.deleteCharAt(rawStr.length() - 1).toString();
    }

    public static String checkRequestBySign(Long timestamp, String nonce, String sign, Map<String, String[]> params, String publicKey) {
        long now = System.currentTimeMillis();
        if (timestamp == null || timestamp > (now + 60000) || timestamp < (now - 60000)) {
            return "无效的时间戳";
        }
        if (nonce == null || nonce.length() < 6 || nonce.length() > 40) {
            return "无效的随机数";
        }
        if (sign == null || sign.length() == 0) {
            return "签名为非空";
        }

        String raw = EncryptionUtil.sortParams(params, "sign");
        String rawMd5 = EncryptionUtil.md5AsHex(raw);

        boolean success = EncryptionUtil.verifyRsa(rawMd5, publicKey, sign);
        if (!success) {
            return "验签失败";
        }
        return null;
    }

}
